package reservation.v1;

import java.io.*;
import java.text.*;
import java.util.*;


/**
 * Classe SerializeObject : Enregistrement de données dans un fichier texte
 * 
 * @author Alexandre Boursier and Jerome Boursier
 * @version 2011.11.20
 */


public abstract class SerializeObject implements Serializable{

    private static final long serialVersionUID = -841808558647138010L;

    /**
     * Liste des trajets
     */
    private static LinkedList<Trajet> trajets = new LinkedList<Trajet>();

    /**
     * Liste des transports 
     */
    private static LinkedList<Transport> transports = new LinkedList<Transport>();

    /**
     * Liste de listes contenant les trajets et les transports
     */
    public static LinkedList<List> tout = new LinkedList<List>();

    /**
     * Nom du fichier dans lequel seront enregistrées les données de réservation, transport et trajet
     */
    private static final String fichier = "Reservations_v1.data";

    /**
     * Fermer le flux de sérialisation courant
     */
    public static void close()
    {
        trajets.clear();
        transports.clear();
    }

    /**
     * Serialiser un object dans un fichier
     * 
     * @param data
     * @return true ou false
     */
    public static boolean store(Object data) {    
        FileOutputStream f= null;
        ObjectOutputStream out= null;


        // Controler l'existence de la donnee
        if (data == null) return false;

        // Construire un fichier logique et le fichier physique associe
        try {f= new FileOutputStream(fichier);}
        catch (Exception e) {return false;}

        // Construire un flux de sortie base sur le fichier logique
        try {out= new ObjectOutputStream(f);}
        catch (Exception e) {return false;}

        // Serialiser l'objet contenant les donnees dans le flux de sortie
        try{out.writeObject(data);}
        catch (Exception e) {return false;}

        try {
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return true;
    }

    /**
     * Déserialiser un object sous forme de liste
     * @return
     */
    public static Object load() {     
        FileInputStream f = null;
        ObjectInputStream in = null;
        Object resultat = null;

        // Construire un fichier logique correspondant
        try {f = new FileInputStream(fichier);}
        catch (Exception e1) {return null;} 

        // Construire un flux d'entrée base sur le fichier logique
        try {in = new ObjectInputStream(f);}
        catch (Exception e) {return null;} 



        // Acquerir et désérialiser le flux d'entrée
        try{
            resultat = in.readObject();


            List<?> t = (List<?>) resultat;

            for(int i = 0; i<t.size(); i++){
                if(((LinkedList<?>) t.get(i)).size() != 0){
                    if(((LinkedList<?>) t.get(i)).get(0) instanceof Trajet){
                        trajets = (LinkedList<Trajet>) t.get(i);
                    }else if(((LinkedList<?>) t.get(i)).get(0) instanceof Transport){
                        transports = (LinkedList<Transport>) t.get(i);
                    }
                }
            }

            SerializeObject.update();
            SerializeObject.store(tout);

        }
        catch (Exception e) {e.printStackTrace(); return null;} 

        // Fermer le flux d'entrée base sur le fichier logique
        try{in.close();}
        catch (Exception e){return null;}


        return resultat;
    }

    /**
     * Contrôler l'existence d'un fichier à partir de son chemin
     */
    public static boolean isExist() {

        // Obtenir une description du fichier a créer
        File f = new File(fichier);

        // Contrôler l'existence du fichier
        if (f.exists())
            return true;
        else return false;
    }

    /**
     * Mettre à jour la liste de listes
     */
    public static void update(){
        tout.clear();
        tout.add(trajets);
        tout.add(transports);
    }

    /**
     * Charger un trajet : l'ajouter à la liste des trajets
     * 
     * @param t
     *            Trajet à charger
     */
    public static void loadTrajet(Trajet t) {
        SerializeObject.update();
        SerializeObject.store(tout);
    }

    /**
     * Charger un transport : l'ajouter à la liste des transports
     * 
     * @param t
     *            Transport à charger
     */
    public static void loadTransport(Transport t) {
        SerializeObject.update();
        SerializeObject.store(tout);
    }

    /**
     * Liste des trajets
     */
    public static String listeTrajets() {
        StringBuilder sb = new StringBuilder();
        for (Trajet t : trajets) {
            sb.append(t.toString());
        }
        return sb.toString();
    }

    /**
     * Liste des transports
     */
    public static String listeTransports() {
        StringBuilder sb = new StringBuilder();
        for (Transport t : transports) {
            sb.append(t.toString());
        }
        return sb.toString();
    }

    /**
     * Créer un nouveau fichier de données vierge à partir de son chemin
     * 
     * @return true or false
     * @throws IOException
     */
    public static boolean creerFichier() throws IOException {
        // Obtenir une description du fichier a créer
        File f = new File(fichier);

        // Contrôler l'éventuelle existence du fichier
        if (f.exists())
            f.delete();

        // Créer le fichier
        try {f.createNewFile();}
        catch(IOException e){return false;}

        return true;
    }

    /**
     * Overloading store : Méthode store pour l'objet de type transport
     * 
     * @param t
     *          Le transport à ajouter dans le fichier texte
     */
    public static boolean store(Transport t){
        transports.add(t);
        SerializeObject.loadTransport(t);
        return true;
    }

    /**
     * Overloading : Méthode store pour l'objet de type transport
     * 
     * @param t
     *          Le trajet à ajouter dans le fichier texte
     */
    public static boolean store(Trajet t){
        trajets.add(t);
        SerializeObject.loadTrajet(t);
        return true;
    }

    /**
     *  Overloading store : Ajouter un client dans un trajet donné
     *  
     * @param c 
     *          Le client cible
     * @parem t
     *          Le trajet cible
     * 
     * @return true si le voyageur est bien ajouté, false sinon (existe déjà)
     * @throws IOException 
     *  
     */
    public static boolean store(Client c, Trajet t){
        if(t.exists(c))
        {
            System.out.println("Le client d'identifiant " + c.getIdentifiant() + " est déjà inscrit");
            return false;
        }  
        if(!t.existPlaces())
        {
            System.out.println("Il ne reste plus de places disponibles pour ce trajet");
            return false;
        }

        for(Trajet tr : trajets)
        {
            if (tr.getIdentTrajet() == t.getIdentTrajet())
                tr.getVoyageurs().add(c);
        }

        SerializeObject.update();
        SerializeObject.store(tout);
        return true;
    }

    /**
     * Supprimer le voyageur cible dans le trajet cible
     * @param c
     *          Le client recherche
     * @param t
     *          Le trajet cible
     * @return  true si le voyageur est supprimé, false sinon (le voyageur n'existe pas dans le trajet)
     */
    public static boolean delete(Client c, Trajet t){
        // Supprimer le client de la liste courante de la classe éponyme
        for(int i = 0; i<trajets.size(); i++)
        {
            Trajet tr = trajets.get(i);
            // Vérifier que c'est le bon trajet
            if(tr.getIdentTrajet() == t.getIdentTrajet())
            {
                // Parcourir la liste des clients cibles
                for(int j = 0; j<tr.getVoyageurs().size(); j++)
                {
                    Client cl = tr.getVoyageurs().get(j);
                    // Vérifier que c'est le bon client
                    if(cl.getIdentifiant() == c.getIdentifiant())
                    {
                        // Supprimer le client de la liste
                        tr.getVoyageurs().remove(j);
                        SerializeObject.update();
                        return true;
                    }
                }
            }
        }

        System.out.println("Le client d'identifiant " + c.getIdentifiant() + " n'a pas été trouvé dans ce trajet");
        return false;
    }

    /**
     * Supprimer le trajet cible
     * @param t
     *          Le trajet cible
     * @return  true si le trajet existe, false sinon
     */
    public static boolean delete(Trajet t){
        // Supprimer le client de la liste courante de la classe éponyme
        for(int i = 0; i<trajets.size(); i++)
        {
            Trajet tr = trajets.get(i);
            // Vérifier que c'est le bon trajet
            if(tr.getIdentTrajet() == t.getIdentTrajet())
            {
                // Parcourir la liste des clients
                for(int j = 0; j<tr.getVoyageurs().size(); j++)
                {
                    //Client cl = tr.getVoyageurs().get(j);
                    // Supprimer les clients de la liste
                    tr.getVoyageurs().remove(j);
                }
                trajets.remove(i);
                SerializeObject.update();
                return true;
            }
        }

        System.out.println("Le trajet n'a pas été trouvé");
        return false;
    }

    /**
     * Supprimer le transport cible
     * @param t
     *          Le transport cible
     * @return true si le transport existe, false sinon
     */
    public static boolean delete(Transport t){
        // Supprimer le client de la liste courante de la classe éponyme
        for(int i = 0; i<transports.size(); i++)
        {
            Transport tp = transports.get(i);
            // Vérifier que c'est le bon transport
            if(tp.getIdentTransport() == t.getIdentTransport())
            {
                // Parcourir la liste des trajets
                for(int j = 0; j<trajets.size(); j++)
                {
                    Trajet tr = trajets.get(j);
                    // Supprimer les trajets associés
                    if(tp.getIdentTransport() == tr.getIdentTrajet())
                    {
                        trajets.remove(j);
                    }
                }
                transports.remove(i);
                SerializeObject.update();
                return true;
            }
        }
        System.out.println("Le transport recherché n'existe pas !");
        return false;
    }

    /**
     * Modifier le transport cible
     * @param t
     *          Le transport cible
     * @return true or false
     */
    public static boolean alter(Transport t){
        for(int i=0; i<transports.size(); i++)
        {
            Transport tp = transports.get(i);
            // Vérifier que c'est le bon trajet
            if(tp.getIdentTransport() == t.getIdentTransport())
            {
                transports.remove(i);
                store(t);
                return true;
            }
        }
        System.out.println("Le transport recherché n'existe pas !");
        return false;
    }

    /**
     * Modifier le trajet cible
     * @param t
     *          Le trajet cible
     * @return true or false
     */
    public static boolean alter(Trajet t){
        for(int i=0; i<trajets.size(); i++)
        {
            Trajet tr = trajets.get(i);
            // Vérifier que c'est le bon trajet
            if(tr.getIdentTrajet() == t.getIdentTrajet())
            {
                trajets.remove(i);
                SerializeObject.store(t);
                return true;
            }
        }
        System.out.println("Le trajet recherché n'existe pas !");
        return false;
    }

    /**
     * Retrouver un transport par un son identifiant
     * 
     * @param id
     * @return le transport s'il est trouvé, null sinon;
     */
    public static Transport getTransportById(int id)
    {
        Transport found = null;
        for(Transport tmp : transports)
        {
            if(tmp.getIdentTransport() == id)
                found = tmp;
        }
        return found;
    } 

    /**
     * Retrouver un transport par un son identifiant
     * 
     * @param id
     * @return le trajet s'il est trouvé, null sinon;
     */
    public static Trajet getTrajetById(int id)
    {
        Trajet found = null;
        for(Trajet tmp : trajets)
        {
            if(tmp.getIdentTrajet() == id)
                found = tmp;
        }
        return found;
    }

    /**
     * Récupérer tous les trajets ou un client est désigné voyageur
     * 
     * @param c
     *          Le client recherché
     * @return la liste des trajets retrouvés
     */
    public static List<Trajet> getTrajetsByClient(Client c)
    {
        List<Trajet> resultat = new ArrayList<Trajet>();
        for (Trajet t : trajets)
        {
            if(t.exists(c))
                resultat.add(t);
        }
        if(resultat.size() == 0)
            System.out.println("Aucun résultat trouvé pour le client de login " + c.getIdentifiant());
        return resultat;
    }

    /**
     * Récupérer tous les trajets qu'un transport effectue
     * 
     * @param t
     *          Le transport recherché
     * @return la liste des transports retrouvés
     */
    public static List<Trajet> getTrajetsByTransport(Transport t)
    {
        List<Trajet> resultat = new ArrayList<Trajet>();
        for (Trajet tr : trajets)
        {
            if(tr.getTransport().getIdentTransport() == t.getIdentTransport())
                resultat.add(tr);
        }
        return resultat;
    }

    /**
     * Récupérer les transports qui ont une place disponible pour un trajet d'une date donnée
     * @param d
     *          La date cible
     * @return la liste des transports trouvés
     */
    public static List<Transport> getTransportsByDate(Date d)
    {
        List<Transport> resultat = new ArrayList<Transport>();
        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
        for (Trajet tr : trajets)
        {

            if(tr.existPlaces() && 
                    String.valueOf(tr.getDate()).equals(String.valueOf(dateFormat.format(d)))
                    && !resultat.contains(tr.getTransport()))
                resultat.add(tr.getTransport());
        }
        return resultat;
    }

    /**
     * Récupérer les transports qui ont une place disponible pour un départ dans une ville donnée
     * @param v
     *          La ville cible
     * @return la liste des transports trouvés
     */
    public static List<Transport> getTransportsByVille(Ville v)
    {
        List<Transport> resultat = new ArrayList<Transport>();
        for (Trajet tr : trajets)
        {
            if(tr.existPlaces() && tr.getDepart().equals(v) && !resultat.contains(tr.getTransport()))
                resultat.add(tr.getTransport());
        }
        return resultat;
    }

    /**
     * Récupérer les trajets qui ont la même date
     * @param d
     *          La date cible
     * @return la liste des trajets trouvés
     */
    public static List<Trajet> getTrajetsByDate(Date d)
    {
        List<Trajet> resultat = new ArrayList<Trajet>();
        DateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy à HH:mm");
        for (Trajet tr : trajets)
        {
            if(String.valueOf(tr.getDate()).equals(String.valueOf(dateFormat.format(d))))
                resultat.add(tr);
        }
        return resultat;
    }

    /**
     * Récupérer les trajets qui ont la même ville de départ
     * @param v
     *          La ville cible
     * @return la liste des trajets trouvés
     */
    public static List<Trajet> getTrajetsByVille(Ville v)
    {
        List<Trajet> resultat = new ArrayList<Trajet>();
        for (Trajet tr : trajets)
        {
            if(String.valueOf(tr.getDepart()).equals(String.valueOf(v)))
                resultat.add(tr);
        }
        return resultat;
    }

}
